package com.example.springmvc;

import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

@RequestMapping("/param")
@RestController
public class ParamController {
    @RequestMapping("/m1")
    public String m1(String name){
        // 不管前端是怎么传过来的
        // 传递单个参数的时候 直接去接收, 传入的时候 参数名字一定要对应上
        return "接收到的参数name: "+name;
    }

    // 多个参数
    @RequestMapping("/m2")
    public String m2(String name,Integer age){
        // 不管前端是怎么传过来的
        // 传递单个参数的时候 直接去接收, 传入的时候 参数名字一定要对应上
        return "接收到的参数name: "+name+" ,age: "+age;
        // 底层逻辑 就是从请求的参数中国, 获取参数名 为 name 的值, 并给 name 赋值
        // 顺序是可以调换的
    }

    @RequestMapping("/m3")
    public String m3(String name, int age){
        return "接收到的参数name: "+name+" ,age: "+age;
        // 这时候 如果 name 不传入值的话,这时候 age 是一个基本类型, 不能够传递一个 null 值
        // 基本类型必须要传值
        // 所以尽量使用包装类型的变量

    }

    // 其实这种传参数的方法也有一个弊端, 那就是每一次传入新的参数的时候, 都要去改变一下新的接口
    // 我们避免这种情况, 我们需要把参数封装成一个对象

    // 传递一个对象
    @RequestMapping("/m4")
    public String m4(Person person){
        return "接收到的参数 person:"+person.toString();
    }
    // 封装成对象 直接穿过来就可以了, spring 已经都封装好了
    // 开发中接口的参数通常定义为 对象

    // 后端参数重命名
    @RequestMapping("/m5")
    public String m5(@RequestParam(value = "name",required = false) String username){
        return "接收到的参数name: "+username;
        // 通过这个RequestParam注解来去修改 前端传过来的参数名字
        // 如果使用了这个注解, 就变成了比传参数
        // 默认是必传
        // 如果不想要的话 就需要在参数中加上required = false选项

        // 如果进行了重命名之后, 就必须要使用 @RequestParam注解里面的 名字
        // username 是不能传过来的

        // username 才是后端的参数, name 才是前端的参数
        // 只是把 name 重命名为 username了, 在后端修改的时候 用 username 为了与其他的名字有更好的区分
        // 但是在前端传递参数的时候还是传name
    }

    // 传递数组
    @RequestMapping("/m6")
    public String m6(String[] arrayParam){
        return "接收到的参数arrayParam: "+ Arrays.toString(arrayParam)+" ,长度"+arrayParam.length;
        // 传递数组, 通过 请求中同一个参数有多个时, 浏览器就会帮我们封装成一个数组
    }

    // 接收集合
    @RequestMapping("/m7")
    public String m7(@RequestParam(required = false) List<String> listParam){
        return "接收到的参数listParam: "+ listParam+" ,长度"+listParam.size();
        // 传递数组, 通过 请求中同一个参数有多个时, 浏览器就会帮我们封装成一个数组
        //如果 看到 5 开头的错误, 先看后端代码的日志

        // 所以, 如果要想传入集合, 就需要@RequestParam注解
    }

    // 使用 JSON 的方式 传递
    @RequestMapping("/m8")
    public String m8(@RequestBody Person person){
        return "接收到的 person: "+person.toString();
        // 然后在 Postman 里面 传递 json 的格式
        // 传递完之后, 发送 会显示为 null
        // 为什么呢?
        //  我们定义的是 person 这样的对像, 或者其他类似这样的一个对象
        // 它会默认 参数 是 Key / Value 型的
        // 它会根据这个 key 跟 对象里面的属性 经行匹配
        //  当前的 数据 没有 key 只有一个数据, 我们希望把当前的JSON数据转换成一个对象
        // 解决方法: 加上一个注解
        // 这时候就接收到 JSON 数据了

        // 那么使用 这个注解的注意事项是
        // 数据必须放到请求正文里面的, 把整个请求正文, 全部转换成一个对象

        // 变量是我们后端来定义de
        // json 是只有 value 的 没有 key 的

    }

    // 获取url 里面的参数
    // 这里加入是获取 userId
    @RequestMapping("/m9/{userId}") // 这里后面要传递url的格式, 要注意这个格式里的名字要和参数中的名字保持一致
    public String m9(@PathVariable Integer userId){
        return "userId: "+userId;
    }
    // 通过这种方式来获取
    // url 也需要满足这种格式 127.0.0.1:8080/param/m9/1233
    //                    里面的 m9/1233
    // 同时也可以获取多个
    @RequestMapping("/m91/{userId}/{name}") // 这里后面要传递url的格式, 要注意这个格式里的名字要和参数中的名字保持一致
    public String m91(@PathVariable Integer userId,@PathVariable("name") String username){
        return "userId: "+userId+" name: "+username;
        // 同样也需要保持一致
        // url 中传递的类型要和@RequestMapping里面写入的路径格式一致 (请求格式必须要和后端url的格式保持一致)
        //  @RequestMapping 的 路径中的名字 要和 方法的参数名字 保持一致
        // 同时也要注意, 每传递一个 参数 都要加上一个注解, 也要保持顺序的一致
        // 也可以进行重命名
        // 这时候RequestMapping 中的 路径名字要和 注解中 的名字保持一致
    }


    // 上传文件@RequestPart
    @RequestMapping("/m10")
    public String m10(@RequestPart MultipartFile file) throws IOException { // 要用 MultipartFile 这样的来接受, 也必须加上@RequestPart注解
        // 接下来上传文件
        System.out.println(file.getOriginalFilename()); // 打印文件的名字
        // 进行保存
        // 可以把他保存到当前的机器上
        file.transferTo(new File("D:/浏览器下载的东西/"+file.getOriginalFilename()));
        // 返回 成功
        return "success";
        // 上传图片的注意事项
        // 使用 postman 进行请求
        // 上传的参数的名字 在 postman 中 body 部分的 form-data 里面输入的 key 值名字 要和 后端的这个 写入参数名字保持一致
        //            (要选择 File 类型的上传)

    }

    // 获取 Cookie

    @RequestMapping("/getCookie")
    public  String getCookie(HttpServletRequest request /*, HttpServletResponse response*/){
        // 这两个参数是 Spring 的内置对象
        // 当需要时 直接 在 方法声明上加上就可以了, 可以加其中一个, 也可以加两个
        //  而 cookie 是从 HttpServletRequest request 中来

        // 之前servlet的获取方法
        Cookie[] cookies =  request.getCookies();
        // 接下来打印
        /*for (Cookie cookie : cookies) {
            System.out.println(cookie.getName()+":"+cookie.getValue());
        }*/

        // 用 lambda 表达式的写法
        if(cookies != null) {
            Arrays.stream(cookies).forEach(cookie -> {
                System.out.println(cookie.getName() + ":" + cookie.getValue());
            });
        }

        return "获取cookie成功";
        // 用原始的 cookie来获取, 是通过 浏览器控制台中的 Application 里面的 cookie , 在里面输入自己的值
        // Cookie 是可以伪造的
    }

    // 接下来看 Spring 获取 cookie 的方式

    @RequestMapping("/getCookie2")
    public  String getCookie2(@CookieValue String xiaozuo , @CookieValue String xiaoren){
        return "cookie存取的值: xiaozuo"+xiaozuo+"  Cookie中存取的值: xiaoren "+xiaoren;
        // 会发现, 重启服务器之后, 刚才伪造的 cookie 还在
        // 用这种方式 需要 用到 @CookieValue 注解
        // 通过与上面的方式对比:
        // 通过 Servlet 方式获取的 Cookie 是获取的全部
        // 而通过注解的方式来获取 Cookie 是 只能拿到其中的一个 Cookie
        // 也可以在参数中写入多个 注解
        // 而且参数中的名字 必须是 Cookie 中存在的
    }


    // 获取 Session


    // 依然先看一下Servlet获取的方式
    // 先创建一个 session
    @RequestMapping("/setSession")
    public  String setSession(HttpServletRequest request){
        HttpSession session = request.getSession();
        session.setAttribute("username","xiaozuo");
        return "success";
    }
    @RequestMapping("/getSession")
    public String getSession(HttpServletRequest request){
        HttpSession session = request.getSession(false);
        // getSession 默认 是 true , 如果没有 Session 会 创建一个 session
        if (session != null){
            String username = (String) session.getAttribute("username"); // 假设 Session 里面存放的是一个 用户名
            // 根据 SessionId 获取 Session对象
            return "登录用户:"+username;
        }
        return "session 为空";
    }


    // 接下来 通过 Spring 来获取
    @RequestMapping("/getSession2")
    public String getSession2(@SessionAttribute(required = false) String username){
        return "username: "+username;
        // 但是 这时候重启服务器, 之前的 Session 就会清空的
        // 因为Session 默认是在服务器上的
        // 同理, 这里也是 一次只能获取一个
        // 多个获取就要加多个注解
        // 这里面默认Session是必传的
    }

    // 第三种方式 , 介于两个之间
    @RequestMapping("/getSession3")
    public String getSession3(HttpSession session){
        String username = (String) session.getAttribute("username");
        return "登录用户: "+ username;

        // HttpSession session 等同于 HttpSession session = request.getSession(true);
        // 这也是第三个 内置对象

        // Session 会 存在 分布式(就是部署在多台机器上)的问题, 在企业开发中, 即使流量再少的服务都需要 2 台机器, 避免单点故障

        // 一个客户端在 服务器A 上发起登录请求, A创建了 Session 并保存
        // 用户继续访问, 如果访问到了另一台机器B,但是服务器B没有Session
        // 所以就不会给客户端提供服务
    }


    // 获取 Header

    // 还是先看 传统方式
    @RequestMapping("/getHeader")
    public String getHeader(HttpServletRequest request){
       String userAgent =  request.getHeader("User-Agent");
       return "userAgent: "+userAgent;
    }

    // 接下来通过 Spring 的方式
    @RequestMapping("/getHeader2")
    public String getHeader2(@RequestHeader("User-Agent") String userAgent){
        return "userAgent: "+userAgent;

        // @RequestHeader("") 里面的值是要和 http 请求中的值对应上的
        // 拿到这个值之后 想赋值给谁 后面在跟上就可以了
    }

}
